# pragma once
#include <vector>
#include <functional>
#include <Arduino.h>
# include<ArduinoJson.h>
#include <Ticker.h>
#include "global.hpp"
#include "message.hpp"

class NodeManager {
    private:
    Ticker ticker_maintain, ticker_hearbeat;
    std::vector<String> name;
    std::vector<uint32_t> ts;

    public:

    void start(function<void(Message&)> call_message_rpc) {
        ticker_hearbeat.attach_ms_scheduled(1000 + random(1000), [call_message_rpc](){
            //Serial.println("NodeManager brocasting heartbeat");
            Message message(TARGET_BROCAST, ACTION_HEARTBEAT);
            message.add_arg(WORKER_NAME);
            call_message_rpc(message);
        });



        ticker_maintain.attach_ms_scheduled(10000 + random(1000), [this](){
            this->maintain();
        });
    }
    
    void update(String worker) {
        //Serial.printf("update node (%s)\n", worker.c_str());
        // update
        for (int i=0; i<name.size(); i++) {
            if (name[i] == worker) {
                ts[i] = millis();
                return ;
            }
        }
        // add
        name.push_back(worker);
        ts.push_back(millis());
    }

    void maintain() {
        //Serial.printf("NodeManager maintain at (%.2f)ms\n", millis() % 100000 / 1000.0);
        int n = name.size();
        int m = 0;
        uint32_t cur = millis(); // ms
        for (int i=0; i<n; i++) {
            if (cur - ts[i] <= 10000) { // <10s, keep
                name[m] = name[i];
                ts[m] = ts[i];
                m++;
            }
        }
        name.resize(m);
        ts.resize(m);
    }

    String dump_json() {
        StaticJsonDocument<768> buf;
        uint32_t cur = millis(); // ms
        buf["self"] = WORKER_NAME;
        for (int i=0; i<name.size(); i++) {
            float delta = (cur - ts[i]) / 1000.0;
            buf[name[i]] = delta;
        }
        String tmp;
        serializeJsonPretty(buf, tmp);
        return tmp;
    }

    bool valid_node(String _name) {
        for (auto _n : name) {
            if (_n == _name) {
                return true;
            }
        }
        return false;
    }
    
}node_mgr;